using System;
using EarthUtility;
using Unity.Mathematics;
using Geometry;

namespace EarthGeospatial
{
    public class GeographicProjection
    {
        Ellipsoid _ellipsoid;
        double _semimajorAxis;
        double _oneOverSemimajorAxis;

        /**
 * @brief The maximum bounding rectangle of the geographic projection,
 * ranging from -PI to PI radians longitude and
 * from -PI/2 to +PI/2 radians latitude.
 */
        public static GlobeRectangle MAXIMUM_GLOBE_RECTANGLE = new GlobeRectangle(
             -Mathd.ONE_PI,
             -Mathd.PI_OVER_TWO,
             Mathd.ONE_PI,
             Mathd.PI_OVER_TWO);

        /**
         * @brief Computes the maximum rectangle that can be covered with this
         * projection
         *
         * @param ellipsoid The {@link Ellipsoid}. Default value:
         * {@link Ellipsoid::WGS84}.
         * @return The rectangle
         */
        public static Geometry.Rectangle computeMaximumProjectedRectangle(Ellipsoid ellipsoid)
        {
            double longitudeValue =
               ellipsoid.getMaximumRadius() * Mathd.ONE_PI;
            double latitudeValue =
               ellipsoid.getMaximumRadius() * Mathd.PI_OVER_TWO;
            return new Geometry.Rectangle(
                -longitudeValue,
                -latitudeValue,
                longitudeValue,
                latitudeValue);
        }
        public GeographicProjection():this(Ellipsoid.WGS84){}

        public GeographicProjection(Ellipsoid ellipsoid)
        {
            _ellipsoid = ellipsoid;
            _semimajorAxis = ellipsoid.getMaximumRadius();
            _oneOverSemimajorAxis = 1.0 / ellipsoid.getMaximumRadius();
        }
        Ellipsoid getEllipsoid()
        {
            return this._ellipsoid;
        }

        /**
   * @brief Converts geodedic ellipsoid coordinates to geographic coordinates.
   *
   * Converts geodetic ellipsoid coordinates, in radians, to the equivalent
   * geographic X, Y, Z coordinates expressed in meters.  The height is copied
   * unmodified to the `z` coordinate.
   *
   * @param cartographic The geodetic coordinates in radians.
   * @returns The equivalent geographic X, Y, Z coordinates, in meters.
   */
     public   double3 project(Cartographic cartographic)
        {
            double semimajorAxis = this._semimajorAxis;
            return new double3(
                cartographic.longitude * semimajorAxis,
                cartographic.latitude * semimajorAxis,
                cartographic.height);
        }

        /**
         * @brief Projects a globe rectangle to geographic coordinates.
         *
         * This is done by projecting the southwest and northeast corners.
         *
         * @param rectangle The globe rectangle to project.
         * @return The projected rectangle.
         */
        public Rectangle project(GlobeRectangle rectangle)
        {
            double3 sw = this.project(rectangle.getSouthwest());
            double3 ne = this.project(rectangle.getNortheast());
            return new Rectangle(sw.x, sw.y, ne.x, ne.y);

        }

       public Cartographic unproject(
            double2 projectedCoordinates)
        {
            double oneOverEarthSemimajorAxis = this._oneOverSemimajorAxis;

            return new Cartographic(
                projectedCoordinates.x * oneOverEarthSemimajorAxis,
                projectedCoordinates.y * oneOverEarthSemimajorAxis,
                0.0);
        }

public        Cartographic unproject(
            double3 projectedCoordinates)
        {
            Cartographic result = this.unproject(projectedCoordinates.xy);
            result.height = projectedCoordinates.z;
            return result;
        }

    public     GlobeRectangle unproject(Rectangle rectangle)
        {
            Cartographic sw = this.unproject(rectangle.getLowerLeft());
            Cartographic ne = this.unproject(rectangle.getUpperRight());
            return new GlobeRectangle(sw.longitude, sw.latitude, ne.longitude, ne.latitude);
        }

    }
}